package com.comleader.ldmapdownload.util.map;

import com.comleader.ldmapdownload.bean.Hopper;
import com.comleader.ldmapdownload.util.Global;
import com.graphhopper.GraphHopper;
import com.graphhopper.reader.osm.GraphHopperOSM;
import com.graphhopper.routing.util.EncodingManager;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.io.File;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.*;

/**
 * @Author: Daisen.Z
 * @Date: 2021/2/26 15:17
 * @Version: 1.0
 * @Description: 创建GraphHopper连接池的工厂方法
 */
@Component
@PropertySource(value = {"classpath:config/download-map.properties"}, encoding = "UTF-8")
public class HopperDataSourceFactory {

    @Value("${map.oms.pbf}")
    private String OMS_PATH_CONFIG;  // 路网数据

    private static String OMS_PATH;

    @Value("${map.oms.graphhopper}")
    private String HOPPER_PATH_CONFIG; // 导航数据存储路径，根据路网数据计算得出

    private static String HOPPER_PATH;

    // 最外面的key是出行方式，里面的key是导航策略，最后获取GraphHopper用以做线路规划,通过dataSources.get("导航策略").get("出行方式")的方法获得GraphHopper
    private static Map<String,Map<String, GraphHopper>> dataSources = new ConcurrentHashMap<>();


    //当容器实例化当前受管Bean时@PostConstruct注解的方法会被自动触发，借此来实现静态变量初始化
    @PostConstruct
    public void init() throws Exception {
        this.OMS_PATH = OMS_PATH_CONFIG;
        this.HOPPER_PATH = HOPPER_PATH_CONFIG;
        // 导航策略
        Class<Global.Weighting> weightingClass = Global.Weighting.class;
        Field[] weightings = weightingClass.getFields();
        // 出行方式
        Class<Global.Vehicle> vehicleClass = Global.Vehicle.class;
        Field[] vehicles = vehicleClass.getFields();

        List<ImportOrLoadGraphHopperTask> importTasks = new ArrayList<>();

        // 初始化加载出行方式
        for (Field vehicle : vehicles) {
            // 出行方式作为大key
            String vehicleValue = String.valueOf(vehicle.get(vehicleClass));
            // 这里先给初始值的原因是怕
            ConcurrentHashMap<String, GraphHopper> valueMap = new ConcurrentHashMap<>();
            dataSources.put(vehicleValue,valueMap);
            // 初始化加载导航策略
            for (Field weighting : weightings) {
                // 获取导航策略作为小key
                String weightingValue = String.valueOf(weighting.get(weightingClass));
                // 创建异步执行任务，加入集合，后边调用
                importTasks.add(new ImportOrLoadGraphHopperTask(weightingValue,vehicleValue));
            }
        }

        // 开始执行异步加载的任务
        // 初始化线程池
        es = Executors.newFixedThreadPool(threadNum);
        ExecutorCompletionService<Object> service = new ExecutorCompletionService<>(es);
        List<Future> results = new ArrayList<>();
        for (ImportOrLoadGraphHopperTask importTask : importTasks) {
            Future submit = service.submit(importTask, 1);
            results.add(submit);
        }
        for (Future result : results) {
            result.get();
        }
        System.out.println("HopperDataSourceFactory >>>>> "+"全部加载完成");
    }

    // 获取加载好的路径计算类
    public static GraphHopper getDataSource(String weighting, String vehicle){
        GraphHopper graphHopper = dataSources.get(vehicle).get(weighting);
        if (graphHopper != null){
            return graphHopper;
        }else {
            // 如果不存在则尝试加载
            GraphHopper ghhopper = new GraphHopperOSM().forServer();
            ghhopper.setDataReaderFile(OMS_PATH);
            // 在哪里存储graphhopper文件
            String path=HOPPER_PATH+File.separator+weighting+ File.separator +vehicle;
            ghhopper.setGraphHopperLocation(path);
            ghhopper.setEncodingManager(new EncodingManager(vehicle));
            // 现在，如果导入，则可能需要几分钟，或者加载需要几秒钟
            // 当然这取决于您导入的区域
            ghhopper.importOrLoad();
            return ghhopper;
        }
    }

    // 获取加载好的路径计算类
    public static GraphHopper getDataSource(Hopper hopper){
        GraphHopper graphHopper = dataSources.get(hopper.getVehicle()).get(hopper.getWeighting());
        if (graphHopper != null){
            return graphHopper;
        }else {
            // 如果不存在则尝试加载
            GraphHopper ghhopper = new GraphHopperOSM().forServer();
            ghhopper.setDataReaderFile(OMS_PATH);
            // 在哪里存储graphhopper文件
            String path=HOPPER_PATH+File.separator+hopper.getVehicle()+ File.separator +hopper.getWeighting();
            ghhopper.setGraphHopperLocation(path);
            ghhopper.setEncodingManager(new EncodingManager(hopper.getVehicle()));
            // 现在，如果导入，则可能需要几分钟，或者加载需要几秒钟
            // 当然这取决于您导入的区域
            ghhopper.importOrLoad();
            return ghhopper;
        }
    }

    // 执行任务的线程池
    private ExecutorService es;
    private int threadNum = 20;

    // 用来多线程导入数据的异步任务
    private class ImportOrLoadGraphHopperTask implements Runnable {

        private String weighting;
        private String vehicle;

        public ImportOrLoadGraphHopperTask(String weighting, String vehicle) {
            this.weighting = weighting;
            this.vehicle = vehicle;
        }

        @Override
        public void run() {
            System.out.println("HopperDataSourceFactory >>>>> "+vehicle+"-"+weighting+"导入中");
            // 创建一个GraphHopper实例
            GraphHopper ghhopper = new GraphHopperOSM().forServer();
            ghhopper.setDataReaderFile(OMS_PATH);
            // 在哪里存储graphhopper文件
            String path=HOPPER_PATH+File.separator+weighting+ File.separator +vehicle;
            ghhopper.setGraphHopperLocation(path);
            ghhopper.setEncodingManager(new EncodingManager(vehicle));
            // 现在，如果导入，则可能需要几分钟，或者加载需要几秒钟
            // 当然这取决于您导入的区域
            ghhopper.importOrLoad();

            // 导入完成后加入缓存
            dataSources.get(vehicle).put(weighting,ghhopper);
            System.out.println("HopperDataSourceFactory >>>>> "+vehicle+"-"+weighting+"已导入完成");
        }
    }

}
